﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using PhoneNetTools.Dns.Records;

namespace PhoneNetTools.Dns
{
    /// <summary>
    /// A wrapper that uses the generic <see cref="DnsResolver"/> internally
    /// to provide convenient access to certain kinds of DNS requests to the user.
    /// </summary>
    public class DnsHelper
    {
        /// <summary>
        /// Begins getting a host entry (IPv4 entry) for the given host name.
        /// </summary>
        /// <param name="hostName">Name of the host to perform a query for.</param>
        /// <param name="callback">The callback to invoke when the operation has finished.</param>
        /// <param name="state">A user state to identify the operation.</param>
        /// <returns>An async result.</returns>
        public IAsyncResult BeginGetHostEntry(string hostName, AsyncCallback callback, object state)
        {
            if (string.IsNullOrEmpty(hostName))
            {
                throw new ArgumentException("No valid host name given.");
            }

            // create message
            var message = new Message();
            message.IsRecursionDesired = true;

            // create question
            var question = new Question(QType.A);
            question.Name = hostName;
            message.Questions.Add(question);

            // let the resolver do the work
            var resolver = new DnsResolver();
            var ar = resolver.BeginDnsRequest(message, callback, state);
            return ar;
        }

        /// <summary>
        /// Ends an ongoing get host entry operation.
        /// Throws any pending exceptions.
        /// </summary>
        /// <param name="asyncResult">The async result returned by the <see cref="BeginGetHostEntry"/> method.</param>
        /// <returns></returns>
        public IEnumerable<IPAddress> EndGetHostEntry(IAsyncResult asyncResult)
        {
            var ar = asyncResult as AsyncResult<Message>;
            if (ar == null)
            {
                throw new ArgumentException("Invalid IAsyncResult used to called EndGetHostEntry.");
            }

            // use the async result to end the operation
            var message = ar.EndInvoke();

            // check to see if the message is a valid result
            if (message.ResponseCode != ResponseCode.NoError)
            {
                var errorCode = Enum.GetName(typeof(ResponseCode), message.ResponseCode);
                var errorMessage = string.Format("The server returned an error: {0} (code {1}", errorCode, message.ResponseCode);
                throw new DnsException(errorMessage, message.ResponseCode);
            }

            // create the result
            var result = message.AnswerRecords
                .OfType<ResourceRecordA>()
                .Select(answer => answer.IPAddress)
                .ToList();

            return result;
        }
    }
}
